package com.sq.universe.system.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.sq.universe.base.service.impl.BaseServiceImpl;
import com.sq.universe.system.dao.AutoCodeDao;
import com.sq.universe.system.dao.TableConfigurationDao;
import com.sq.universe.system.entity.AutoCodeEntity;
import com.sq.universe.system.entity.TableConfigurationEntity;
import com.sq.universe.system.service.AutoCodeService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author sunqian 2020.05.25
 */
@Service
@Slf4j
public class AutoCodeServiceImpl extends BaseServiceImpl<AutoCodeEntity, AutoCodeDao> implements AutoCodeService {

    @Autowired
    private AutoCodeDao autoCodeDao;

    @Autowired
    private TableConfigurationDao tableConfigurationDao;

    @Value("${universe.fileBasePath}")
    private String fileBasePath;

    /**
     * 查询所有的表
     *
     * @return
     */
    @Cacheable(value = "common", key = "'tables'")
    public List<Map> getTables() {
        return autoCodeDao.getTables();
    }

    /**
     * 查询选中表的详情
     *
     * @return
     */
    public List<AutoCodeEntity> queryTableDetail(String tableName) {

        TableConfigurationEntity tableConfigurationEntity = new TableConfigurationEntity();
        tableConfigurationEntity.setTcTableName(tableName);
        List<TableConfigurationEntity> list = tableConfigurationDao.query(tableConfigurationEntity);
        if (list.size() > 0) {
            return list.get(0).getAutoCodeList();
        }
        return autoCodeDao.queryTableDetail(tableName);
    }


    /**
     * 单表代码生成
     *
     * @param tableConfigurationEntity
     */
    public String codeGenerateBySingle(TableConfigurationEntity tableConfigurationEntity) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
        Map tableField = autoCodeDao.tableFieldDetail(tableConfigurationEntity.getTcTableName());
        //包名
        String packageName = "com.sq.universe";
        //模板路径
        String templatesDir = "\\autoCode";
        //所属模块
        String projectName = tableConfigurationEntity.getProjectName();
        //所属模块
        String moduleName = tableConfigurationEntity.getModuleName();
        //前缀
        String prefix = tableConfigurationEntity.getPrefix();
        //输出目录
        String outPath = System.getProperty("user.dir");
        if(tableConfigurationEntity.getIsDownload()){
            outPath = fileBasePath + "generate" + File.separator + DateUtil.format(new Date(),"yyyyMMddHHmmssSSS");
            FileUtil.mkParentDirs(outPath);
        }
        String controllerPath = outPath + "\\universe-" + projectName + "\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\controller\\";
        String serviceimplPath = outPath + "\\universe-" + projectName + "\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\service\\impl\\";
        String daoPath = outPath + "\\universe-" + projectName + "\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\dao\\";
        String daoXmlPath = outPath + "\\universe-" + projectName + "\\src\\main\\resources\\mapper\\" + moduleName + "\\";
        String entityPath = outPath + "\\universe-" + projectName + "\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\entity\\";
        String servicePath = outPath + "\\universe-" + projectName + "\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\service\\";

//        String controllerPath = outPath + "\\universe-common\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\controller\\";
//////        String serviceimplPath = outPath + "\\universe-common\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\service\\impl\\";
//////        String daoPath = outPath + "\\universe-common\\src\\main\\java\\com\\sq\\universe\\" + moduleName + "\\dao\\";
//////        String daoXmlPath = outPath + "\\universe-common\\src\\main\\resources\\mapper\\" + moduleName + "\\";
        String authSqlPath = outPath + "\\files\\sql\\";
        String authRouterPath = outPath + "\\files\\router\\";
        String vuePath = tableConfigurationEntity.getTcTableName().replace(prefix, "").replace("_","-");
        String vueListPath = outPath + "\\universe-view\\src\\views\\" + moduleName + "\\" + vuePath + "\\" ;
        String langPath = outPath + "\\files\\lang\\";
        String appPath = outPath + "\\universe-app\\pages\\" + moduleName + "\\" + vuePath + "\\" ;


        //作者
        String author = "孙乾";
        StringBuilder table = new StringBuilder(camelName(tableConfigurationEntity.getTcTableName().replace(prefix, "")));
        table = table.replace(0, 1, table.substring(0, 1).toUpperCase());
        //sql类型转java类型
        tableConfigurationEntity.getAutoCodeList().forEach(autoCodeEntity -> {
            String javaTypeName = this.camelName(autoCodeEntity.getField());
            autoCodeEntity.setJavaField(javaTypeName);
            autoCodeEntity.setJavaType(this.sqlType2javaType(autoCodeEntity.getType()));
            if ("PRI".equals(autoCodeEntity.getKey())) {
                tableConfigurationEntity.setKeyColumn(autoCodeEntity.getField());
                tableConfigurationEntity.setKeyProperty(javaTypeName);
                tableConfigurationEntity.setFieldPrefix(autoCodeEntity.getField().split("_")[0]);
            }
        });
        Map data = new HashMap();
        data.put("author", author);
        data.put("createDate", simpleDateFormat.format(new Date()));
        data.put("moduleName", moduleName);
        data.put("beanName", table);
        data.put("prefix", prefix);
        data.put("objectName", camelName(table.toString()));
        data.put("tableName", tableConfigurationEntity.getTcTableName());
        data.put("Fields", tableConfigurationEntity.getAutoCodeList());
        data.put("tableConfiguration", tableConfigurationEntity);
        data.put("packageName", packageName);
        data.put("tableField", tableField);
        data.put("vuePath", vuePath);
        try {
            //模板路径
            String classpath = this.getClass().getResource("/").getPath() + "\\templates" + templatesDir;
            classpath = java.net.URLDecoder.decode(classpath);
            cfg.setDirectoryForTemplateLoading(new File(classpath));
            //检测文件是否存在
            //实体
            Template template = cfg.getTemplate("entity.ftl");
            File file = new File(entityPath + table + "Entity.java");
            if (!file.exists()) {
                checkFilePath(entityPath);
                try(
                        OutputStream outputStream = new FileOutputStream(entityPath + table + "Entity.java");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                   ) {
                    //实体
                    template = cfg.getTemplate("entity.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("实体生成异常",e);
                }
            }
            // dao
            file = null;
            file = new File(daoPath + table + "Dao.java");
            if (!file.exists()) {
                checkFilePath(daoPath);
                try(
                        OutputStream outputStream = new FileOutputStream(daoPath + table + "Dao.java");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("dao.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("Dao生成异常",e);
                }
            }
            //xml
            file = null;
            file = new File(daoXmlPath + table + "Dao.xml");
            if (!file.exists()) {
                checkFilePath(daoXmlPath);
                try(
                        OutputStream outputStream = new FileOutputStream(daoXmlPath + table + "Dao.xml");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("dao-xml.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("xml生成异常",e);
                }
            }
            //service
            file = null;
            file = new File(servicePath + table + "Service.java");
            if (!file.exists()) {
                checkFilePath(servicePath);
                try(
                        OutputStream outputStream = new FileOutputStream(servicePath + table + "Service.java");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("service.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("service生成异常",e);
                }

            }
            //serviceImpl
            file = null;
            file = new File(serviceimplPath + table + "ServiceImpl.java");
            if (!file.exists()) {
                checkFilePath(serviceimplPath);
                try(
                        OutputStream outputStream = new FileOutputStream(serviceimplPath + table + "ServiceImpl.java");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("serviceImpl.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("serviceImpl生成异常",e);
                }
            }
            //controller
            file = null;
            file = new File(controllerPath + table + "Controller.java");
            if (!file.exists()) {
                checkFilePath(controllerPath);
                try(
                        OutputStream outputStream = new FileOutputStream(controllerPath + table + "Controller.java");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("controller.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("controller生成异常",e);
                }

            }

            //权限sql语句
            file = new File(authSqlPath + table + "Auth.sql");
            if (!file.exists()) {
                checkFilePath(authSqlPath);
                try(
                        OutputStream outputStream = new FileOutputStream(authSqlPath + table + "Auth.sql");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("sql.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("sql生成异常",e);
                }
            }

            //列表页面是否存在，存在则不覆盖
            file = new File(vueListPath + vuePath + "-index.vue");
            if (!file.exists()) {
                checkFilePath(vueListPath);
                try(
                        OutputStream outputStream = new FileOutputStream( vueListPath + vuePath + "-index.vue");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("/vue-web/list.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("vue-web/list生成异常",e);
                }
            }

            //编辑页面
            file = new File(vueListPath + vuePath + "-edit.vue");
            if (!file.exists()) {
                checkFilePath(vueListPath);
                try(
                        OutputStream outputStream = new FileOutputStream( vueListPath + vuePath + "-edit.vue");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("/vue-web/edit.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("vue-web/list生成异常",e);
                }
            }

            //路由
            file = new File(authRouterPath + table + "router.txt");
            if (!file.exists()) {
                checkFilePath(authRouterPath);
                try(
                        OutputStream outputStream = new FileOutputStream( authRouterPath + table + "Router.txt");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("/vue-web/router.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("vue-web/router",e);
                }
            }

            //国际化文件
            file = new File(authRouterPath + table + "-lang.txt");
            if (!file.exists()) {
                checkFilePath(langPath);
                try(
                        OutputStream outputStream = new FileOutputStream( langPath + table + "-lang.txt");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("/vue-web/lang.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("vue-web/lang",e);
                }
            }
            //app 主页
            file = new File(appPath + vuePath + "-index.vue");
            if (!file.exists()) {
                checkFilePath(appPath );
                try(
                        OutputStream outputStream = new FileOutputStream( appPath + vuePath + "-list.vue");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("/app/list.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("/app/list.ftl",e);
                }
            }
            //app 编辑页面
            file = new File(appPath + vuePath + "-edit.vue");
            if (!file.exists()) {
                checkFilePath(appPath );
                try(
                        OutputStream outputStream = new FileOutputStream( appPath + vuePath + "-edit.vue");
                        OutputStreamWriter writer =  new OutputStreamWriter(outputStream, "UTF-8");
                ) {
                    template = cfg.getTemplate("/app/edit.ftl");
                    template.process(data, writer);
                }catch (Exception e){
                    log.error("/app/edit.ftl",e);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return outPath;
    }

    public void checkFilePath(String filePath){
        File file = new File(filePath);
        if(!file.exists()){
            file.mkdirs();
        }
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。</br>
     * 例如：HELLO_WORLD->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelName(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线，仅将首字母小写
            return name.substring(0, 1).toLowerCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String camels[] = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 处理真正的驼峰片段
            if (result.length() == 0) {
                // 第一个驼峰片段，全部字母都小写
                result.append(camel.toLowerCase());
            } else {
                // 其他的驼峰片段，首字母大写
                result.append(camel.substring(0, 1).toUpperCase());
                result.append(camel.substring(1).toLowerCase());
            }
        }
        return result.toString();
    }


    /**
     * sql类型转java类型
     *
     * @param sqlType
     * @return
     */
    public static String sqlType2javaType(String sqlType) {
        if (sqlType.indexOf("int") > -1) {
            return "Integer";
        } else if (sqlType.indexOf("varchar") > -1) {
            return "String";
        } else if (sqlType.indexOf("float") > -1) {
            return "String";
        } else if (sqlType.indexOf("double") > -1) {
            return "Double";
        } else if (sqlType.indexOf("double") > -1) {
            return "Double";
        } else if (sqlType.indexOf("decimal") > -1) {
            return "BigDecimal";
        } else if (sqlType.indexOf("date") > -1) {
            return "Date";
        } else if (sqlType.indexOf("time") > -1) {
            return "Date";
        }
        return "String";
    }

}
